package com.ericsson.enap.modules.kpi.thread;

import com.ericsson.common.util.*;
import com.ericsson.enap.modules.enm.entity.EnipEnmKpiAlarmCondition;
import com.ericsson.enap.modules.kpi.entity.*;
import com.ericsson.enap.modules.kpi.service.EnipKpiAlarmService;
import com.ericsson.enap.modules.kpi.service.EnipKpiFileProcess;
import com.ericsson.mq.constants.RabbitMqConsts;
import com.ericsson.mq.domain.EnipKpiResult;
import com.googlecode.aviator.Expression;
import com.ice.tar.TarInputStream;
import org.slf4j.Logger;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

/**
 * VGC五分钟KPI数据处理线程
 */
public class VgcFivKpiDataProcessThread implements Runnable {

    private EnipKpiFileProcess kpiFileProcess;
    private Logger log;
    private MongoTemplate mongoTemplate;
    private List<EnipFormula> formulas;
    private String typeStr;
    private Date date;
    private VgcFifKpi vgcFifKpi;
    private String vgckpiDownloadDir;
    private static final String EXT_GZ = ".gz";
    private RedisCache redisCache;
    private String redisKey="vgc:kpi:five:data:";
    private String lastDownTime="LDT";
    private EnipKpiAlarmService enipKpiAlarmService;
    private String interval;
    private RabbitTemplate rabbitTemplate;
    private Map<Long,EnipEnmKpiAlarmCondition> enipEnmKpiAlarmConditionMap;

    public VgcFivKpiDataProcessThread(EnipKpiAlarmService enipKpiAlarmService,RedisCache redisCache,VgcFifKpi vgcFifKpi, String typeStr, List<EnipFormula> formulas, Date date, Logger log, String vgckpiDownloadDir,RabbitTemplate rabbitTemplate,String interval,Map<Long, EnipEnmKpiAlarmCondition> enipEnmKpiAlarmConditionMap) {
        this.enipKpiAlarmService=enipKpiAlarmService;
        this.redisCache=redisCache;
        this.kpiFileProcess = SpringUtils.getBean("kpiFileProcess" + typeStr + "Impl");
        this.mongoTemplate = (MongoTemplate) SpringUtils.getBean("mongoTemplate");
        this.vgcFifKpi = vgcFifKpi;
        this.typeStr = kpiFileProcess.getNetType().name();
        this.formulas = formulas;
        this.date = date;
        this.log = log;
        this.vgckpiDownloadDir = vgckpiDownloadDir;
        this.rabbitTemplate=rabbitTemplate;
        this.interval=interval;
        this.enipEnmKpiAlarmConditionMap=enipEnmKpiAlarmConditionMap;
    }

    @Override
    public void run() {
        TreeMap<String,List<EnipMeasInfo>> mapResult= getData(0, this.vgcFifKpi);
        for(String key:mapResult.keySet()) {
            List<EnipMeasInfo> curDataObj=mapResult.get(key);
            countAndInsertToDB(curDataObj);
        }
    }

    private TreeMap<String,List<EnipMeasInfo>> getData(int delay, VgcFifKpi vgcFifKpi) {
        TreeMap<String,List<EnipMeasInfo>> mapResult=new TreeMap<>();
        try {
            String filePathString = this.vgckpiDownloadDir + File.separator + this.typeStr + File.separator + vgcFifKpi.getId() + File.separator;
            Map<String, List<File>> filesMap = downloadFilesByFTP(vgcFifKpi, delay, filePathString);
            for (String key : filesMap.keySet()) {
                List<File> files = filesMap.get(key);
                // 需要解压的网元类型
                if (kpiFileProcess.needUncompress()) {
                    this.uncompress(files, filePathString);
                }
                if (files != null && files.size() > 0) {
                    if (kpiFileProcess.needUncompress()) {
                        files.clear();
                        File file = new File(filePathString);
                        File[] dirs = file.listFiles();
                        if (dirs != null) {
                            for (int i = 0; i < dirs.length; i++) {
                                if (!(dirs[i].getName().endsWith(EXT_GZ))) {
                                    files.add(dirs[i]);
                                }
                            }
                        }
                    }
                    if (files.size() > 0) {
                        //存储上一次下载的文件时间
                        this.setRedisCache(redisKey, this.vgcFifKpi.getId().toString() + ":" + lastDownTime, files.get(0).getName().substring(0, 14));
                    }
                    List<EnipMeasInfo> result = filterAndGetData(files);
                    mapResult.put(key,result);
                } else {
                    log.info("download file null");
                }
            }
            } catch(Exception e){
                log.error("KpiDataProcessTask getData method:" + e.getMessage() + "netType is :" + this.typeStr, e);
                e.printStackTrace();
            }
        return mapResult;
    }

    /**
     * 从ftp服务器下载文件
     *
     * @param vgcFifKpi
     * @param delay
     * @return
     * @throws Exception
     */
    private Map<String,List<File>> downloadFilesByFTP(VgcFifKpi vgcFifKpi, int delay, String filePathString) throws Exception {
        File filePath = new File(filePathString);
        FileUtil.newDirs(filePath);
        // 删除上一批次下载的文件
        if (filePath.exists()) {
            File[] oldxmlTargetFiles = filePath.listFiles();
            for (File file : oldxmlTargetFiles) {
                if (file.exists() && file.isFile()) {
                    log.info("|||||||||||||||||||||||||||||||||||||||||||||||" + file.getName());
                    file.delete();
                }
            }
        }
        vgcFifKpi.setLastDownTime(redisCache.getCacheObject(redisKey+this.vgcFifKpi.getId()+":"+lastDownTime));
        return kpiFileProcess.download(vgcFifKpi, filePathString, log,this.date);
    }

    /**
     * 通过各个kpiFileProcess的实现类的analysis方法解析文件
     *
     * @param files
     * @return
     */
    private List<EnipMeasInfo> filterAndGetData(List<File> files) {
        log.info(vgcFifKpi.getName() + "analysis start");
        List<EnipMeasInfo> enipMeasInfoList = kpiFileProcess.analysis(files, log);
        log.info(vgcFifKpi.getName() + "analysis end" + enipMeasInfoList.size());
        return enipMeasInfoList;
    }

    private void countAndInsertToDB(Object curDataObj) {
        if (curDataObj == null) {
            log.error(typeStr + ":" + vgcFifKpi.getName() + "curDataObj null");
            return;
        }
        List<EnipMeasInfo> measInfos = (List<EnipMeasInfo>) curDataObj;
        String beginTime = measInfos.get(0).getBeginTime();
        if(beginTime.equals("")){
            beginTime=DateUtils.getStatisTime(0, date, "yyyy-MM-dd HH:mm:00");
        }
        Query query = Query.query(Criteria.where("dateTime").is(beginTime));
        query.addCriteria(Criteria.where("netid").is(vgcFifKpi.getId().toString()));
        long count = this.mongoTemplate.count(query, "vgckpi_16_" + typeStr.toLowerCase());//统计查询count查询
        log.info("count:" + count + "beginTime:" + beginTime + "netid:" + vgcFifKpi.getId().toString());
        if (count == 0) {
            //循环每一个公式
            Map<String, String> resultMap = new HashMap<>();
            EnipKpiDataResult kpiDataResult = new EnipKpiDataResult();
            try {
                for (EnipFormula formula : formulas) {
                    try {
                        //1 获取编译后的表达式对象
                        Expression compiledExp = formula.getExpression();
                        //2 取出计算因子
                        List<String> vars = compiledExp.getVariableFullNames();
                        //计算公式用的map,key 为计算因子，value 为 对应的值
                        Map<String, Object> dataMap = new HashMap<>();
                        for (String var : vars) {
                            String perVar = FormulaConvert.restoreFormula(var);
                            for (EnipMeasInfo m : measInfos) {
                                String result = parseData(m, perVar,formula);
                                if (null != result && !result.equals("0.0")) {
                                    dataMap.put(var, Double.valueOf(result));
                                }
                            }
                            if (!dataMap.containsKey(var)) {
                                dataMap.put(var, Double.valueOf("0.0"));
                            }
                        }
                        if (dataMap.size() > 0) {
                            Object result = AviatorUtil.execute(compiledExp, dataMap, formula);
                            resultMap.put(String.valueOf(formula.getId()), String.valueOf(result));
                        }
                    } catch (Exception ex) {
                        log.error(typeStr + ":ex:" + vgcFifKpi.getName() + ex.getCause()+ex.getMessage());
                    }
                }
            } catch (Exception e) {
                log.error(typeStr + ":e:" + vgcFifKpi.getName() + e.getCause()+e.getMessage());
            }
            if (resultMap.size() > 0) {
                kpiDataResult.setDateTime(beginTime);
                kpiDataResult.setNetid(String.valueOf(vgcFifKpi.getId()));
                kpiDataResult.setPoolid(String.valueOf(vgcFifKpi.getPool_id()));
                kpiDataResult.setNetName(vgcFifKpi.getName());
                kpiDataResult.setKpiMap(resultMap);
                log.info("insert data to MongoDB " + typeStr + ":" + vgcFifKpi.getName() + " start ...");
                mongoTemplate.insert(kpiDataResult, "vgckpi_16_" + typeStr.toLowerCase());
                log.info("insert data to MongoDB " + typeStr + ":" + vgcFifKpi.getName() + " end ...");
                log.info("insert data to rabbitMQ " + typeStr + ":" + vgcFifKpi.getName() + " start ...");
                sendAlarmDataToMQ(kpiDataResult,enipEnmKpiAlarmConditionMap);
                log.info("insert data to rabbitMQ " + typeStr + ":" + vgcFifKpi.getName() + " end ...");
            } else {
                log.info("nodata：" + vgcFifKpi.getName());
            }
        }
    }

    /**
     * 判断数据 是否配置告警，如配置发送到MQ，如没有不发送
     * @param kpiDataResult
     * @param enipEnmKpiAlarmConditionMap
     */
    private void sendAlarmDataToMQ(EnipKpiDataResult kpiDataResult, Map<Long, EnipEnmKpiAlarmCondition> enipEnmKpiAlarmConditionMap) {
        Map<String,String> kpiAndValueMap=kpiDataResult.getKpiMap();
        for (Map.Entry<String, String> entry : kpiAndValueMap.entrySet()) {
            if(enipEnmKpiAlarmConditionMap.containsKey(Long.parseLong(entry.getKey()))){
                EnipKpiResult enipKpiResult=new EnipKpiResult();
                enipKpiResult.setInterval(Integer.parseInt(this.interval));
                enipKpiResult.setKpiCreateTime(kpiDataResult.getDateTime());
                enipKpiResult.setKpiId(Long.parseLong(entry.getKey()));
                enipKpiResult.setNetId(Long.parseLong(kpiDataResult.getNetid()));
                enipKpiResult.setValue(entry.getValue());
                enipKpiResult.setBizType(enipEnmKpiAlarmConditionMap.get(Long.parseLong(entry.getKey())).getDoWithType());
                rabbitTemplate.convertAndSend(RabbitMqConsts.DIRECT_ROUTING_KEY_ALARM,enipKpiResult);
            }
        }
    }

    /**
     * 根据公式中的规则 解析数据
     *
     * @param m
     * @param perVar
     * @param formula
     * @return
     */
    private String parseData(EnipMeasInfo m, String perVar, EnipFormula formula) {
        if(!formula.getFormula().startsWith("<")) {
            Map<String, EnipMeasValue> mvMap = m.getMeasValueMap();
            String tempVar = perVar;
            int count = 0;
            double calResult = 0;
            perVar = perVar.replace("avg_", "").replace("min_", "");
            if (perVar.contains(".") && m.getMeasTypeMap().containsKey(perVar.split("\\.")[0].replace(FormulaConvertChfFive.__POINT__CHAR__, "."))) {//包含条件
                String[] temp = perVar.split("\\.");
                temp[0] = temp[0].replace(FormulaConvertChfFive.__POINT__CHAR__, ".");
                for (String key : mvMap.keySet()) {
                    boolean remark = true;
                    for (int n = 1; n < temp.length; n++) {
                        if (temp[n].contains(FormulaConvertChfFive.__POINT__CHAR__)) {
                            temp[n] = temp[n].replace(FormulaConvertChfFive.__POINT__CHAR__, ".");
                        }
                        if (!temp[n].contains("|")) {//条件中不包含|
                            if ((!temp[n].contains("not_contains") || !temp[n].contains("not_startingwith")) && !checkKey(key, temp[n].replace("contains", "").replace("startingwith", ""), temp[n])) {
                                remark = false;
                                break;
                            } else if ((temp[n].contains("not_contains") || temp[n].contains("not_startingwith")) && checkKey(key, temp[n].replace("not_contains", "").replace("not_startingwith", ""), temp[n])) {
                                remark = false;
                                break;
                            }
                        } else {//条件中包含|
                            String[] tempOr = temp[n].split("\\|");
                            for (int o = 0; o < tempOr.length; o++) {
                                if ((!tempOr[o].contains("not_contains") || !tempOr[o].contains("not_startingwith")) && checkKey(key, tempOr[o].replace("contains", "").replace("startingwith", ""), tempOr[o])) {
                                    remark = true;
                                    break;
                                } else if ((tempOr[o].contains("not_contains") || tempOr[o].contains("not_startingwith")) && !checkKey(key, tempOr[o].replace("not_contains", "").replace("not_startingwith", ""), tempOr[o])) {
                                    remark = true;
                                    break;
                                } else {
                                    remark = false;
                                }
                            }
                        }
                    }
                    if (remark == true) {
                        EnipMeasValue enipMeasValue = mvMap.get(key);
                        if (enipMeasValue.getRMap().containsKey(m.getMeasTypeMap().get(temp[0]))) {
                            calResult = calResult + Double.parseDouble(enipMeasValue.getRMap().get(m.getMeasTypeMap().get(temp[0])));
                            if (tempVar.contains("avg_")) {//包含计算平均值的  计数器
                                count = count + 1;
                            }
                        }
                    }
                }
                //处理需要和上一组五分钟做差的公式
                if (tempVar.contains("min_")) {
                    Object lastValue = redisCache.getCacheObject(redisKey + this.vgcFifKpi.getId() + ":" + formula.getId() + ":" + tempVar);
                    this.setRedisCache(redisKey, this.vgcFifKpi.getId().toString() + ":" + formula.getId() + ":" + tempVar, calResult);
                    if (null != lastValue) {
                        calResult = calResult - Double.parseDouble(lastValue.toString());
                    }
                }
                if (count > 0) {
                    return calResult / count + "";
                } else {
                    return calResult + "";
                }
            } else {//不包含条件
                if (m.getMeasTypeMap().containsKey(perVar.replace(FormulaConvertChfFive.__POINT__CHAR__, "."))) {
                    for (EnipMeasValue enipMeasValue : mvMap.values()) {
                        if (enipMeasValue.getRMap().containsKey(m.getMeasTypeMap().get(perVar.replace(FormulaConvertChfFive.__POINT__CHAR__, ".")))) {
                            calResult = calResult + Double.parseDouble(enipMeasValue.getRMap().get(m.getMeasTypeMap().get(perVar.replace(FormulaConvertChfFive.__POINT__CHAR__, "."))));
                            if (tempVar.contains("avg_")) {//包含计算平均值的  计数器
                                count = count + 1;
                            }
                        }
                    }
                    //处理需要和上一组五分钟做差的公式
                    if (tempVar.contains("min_")) {
                        Object lastValue = redisCache.getCacheObject(redisKey + this.vgcFifKpi.getId() + ":" + formula.getId() + ":" + tempVar);
                        this.setRedisCache(redisKey, this.vgcFifKpi.getId().toString() + ":" + formula.getId() + ":" + tempVar, calResult);
                        if (null != lastValue) {
                            calResult = calResult - Double.parseDouble(lastValue.toString());
                        }
                    }
                    if (count > 0) {
                        return calResult / count + "";
                    } else {
                        return calResult + "";
                    }
                }
            }
            return null;
        }else{
           return praseRegData(m,perVar,formula);
        }
    }

    /**
     * 处理特殊的，需要判断部分匹配Key的公式
     * @return
     */
    private String praseRegData(EnipMeasInfo m, String perVar, EnipFormula formula){
        Map<String, EnipMeasValue> mvMap = m.getMeasValueMap();
        String tempVar = perVar.replace("<", "").replace(">", "");
        int count = 0;
        double calResult = 0;
        perVar = tempVar.replace("avg_", "").replace("min_", "");
        if (perVar.contains(".") && keyExits(m.getMeasTypeMap(),perVar.split("\\.")[0].replace(FormulaConvertChfFive.__POINT__CHAR__, "."))) {//包含条件
            String[] temp = perVar.split("\\.");
            temp[0] = temp[0].replace(FormulaConvertChfFive.__POINT__CHAR__, ".");
            for (String key : mvMap.keySet()) {
                boolean remark = true;
                for (int n = 1; n < temp.length; n++) {
                    if (temp[n].contains(FormulaConvertChfFive.__POINT__CHAR__)) {
                        temp[n] = temp[n].replace(FormulaConvertChfFive.__POINT__CHAR__, ".");
                    }
                    if (!temp[n].contains("|")) {//条件中不包含|
                        if ((!temp[n].contains("not_contains") || !temp[n].contains("not_startingwith")) && !checkKey(key, temp[n].replace("contains", "").replace("startingwith", ""), temp[n])) {
                            remark = false;
                            break;
                        } else if ((temp[n].contains("not_contains") || temp[n].contains("not_startingwith")) && checkKey(key, temp[n].replace("not_contains", "").replace("not_startingwith", ""), temp[n])) {
                            remark = false;
                            break;
                        }
                    } else {//条件中包含|
                        String[] tempOr = temp[n].split("\\|");
                        for (int o = 0; o < tempOr.length; o++) {
                            if ((!tempOr[o].contains("not_contains") || !tempOr[o].contains("not_startingwith")) && checkKey(key, tempOr[o].replace("contains", "").replace("startingwith", ""), tempOr[o])) {
                                remark = true;
                                break;
                            } else if ((tempOr[o].contains("not_contains") || tempOr[o].contains("not_startingwith")) && !checkKey(key, tempOr[o].replace("not_contains", "").replace("not_startingwith", ""), tempOr[o])) {
                                remark = true;
                                break;
                            } else {
                                remark = false;
                            }
                        }
                    }
                }
                if (remark == true) {
                    EnipMeasValue enipMeasValue = mvMap.get(key);
                    if (enipMeasValue.getMeasObjLdn().contains(temp[0])) {
                        calResult = calResult + Double.parseDouble(enipMeasValue.getRMap().values().stream().findFirst().get());
                        if (tempVar.contains("avg_")) {//包含计算平均值的  计数器
                            count = count + 1;
                        }
                    }
                }
            }
            if (count > 0) {
                return calResult / count + "";
            } else {
                return calResult + "";
            }
        } else {//不包含条件
            if (keyExits(m.getMeasTypeMap(),perVar.replace(FormulaConvertChfFive.__POINT__CHAR__, "."))) {
                for (EnipMeasValue enipMeasValue : mvMap.values()) {
                    if (enipMeasValue.getMeasObjLdn().contains(perVar.replace(FormulaConvertChfFive.__POINT__CHAR__, "."))) {
                        calResult = calResult + Double.parseDouble(enipMeasValue.getRMap().values().stream().findFirst().get());
                        if (tempVar.contains("avg_")) {//包含计算平均值的  计数器
                            count = count + 1;
                        }
                    }
                }
                if (count > 0) {
                    return calResult / count + "";
                } else {
                    return calResult + "";
                }
            }
        }
        return null;
    }

    /**
     * 辅助处理特殊的，需要判断部分匹配Key的公式，判断key列表中是否包含需要判断的key
     * @return
     */
    private boolean keyExits(Map<String,String> measTypeMap,String key){
        Set<String> keySet=measTypeMap.keySet();
        Iterator interator=keySet.iterator();
        while(interator.hasNext()){
            if(interator.next().toString().contains(key)){
                return true;
            }
        }
        return false;
    }

    private boolean checkKey(String key,String value,String valueSelf){
        value=value.replace("{","").replace("}","").trim();
        if(key.contains(",")) {
            String[] keyStr = key.split(",");
            boolean result=false;
            for(int i=0;i<keyStr.length;i++){
                if(valueSelf.contains("contains")){
                    if (keyStr[i].trim().contains(value)) {
                        result = true;
                        break;
                    }
                }else if(valueSelf.contains("startingwith")){
                    if (keyStr[i].trim().startsWith(value)) {
                        result = true;
                        break;
                    }
                }else {
                    if (keyStr[i].trim().equals(value)) {
                        result = true;
                        break;
                    }
                }
            }
            return result;
        }else{
            return key.contains(value);
        }
    }

    /**
     * 解压缩下载的文件
     *
     * @param listFiles
     */
    private void uncompress(List<File> listFiles, String path) {
        log.info("==========================  uncompress start  =========================");
        InputStream inputstream = null;
        OutputStream outputstream = null;
        TarInputStream zis = null;
        GZIPInputStream gzis = null;
        try {
            for (int i = 0; i < listFiles.size(); i++) {
                File file = listFiles.get(i);
                if (file.getName().endsWith(".tar")) {
                    File tempFile = null;
                    inputstream = new FileInputStream(file);
                    zis = new TarInputStream(inputstream);
                    while (zis.getNextEntry() != null) {
                        tempFile = new File(path);
                        tempFile.createNewFile();
                        outputstream = new FileOutputStream(tempFile);
                        byte[] buffer = new byte[1024];
                        int len = 0;
                        while ((len = zis.read(buffer)) > 0) {
                            outputstream.write(buffer, 0, len);
                        }
                    }
                } else if (file.getName().endsWith(EXT_GZ)) {
                    log.info("==========================  uncompress name:"+file.getName());
                    UncompressFileGZIPUtil.doUncompressFile(path, file, file.getName());
                }
            }
        } catch (FileNotFoundException e) {
            log.info(">> AnalyticalXMLServiceImpl uncompress failre.Cause by" + e.getMessage());
        } catch (IOException e) {
            log.info(">> AnalyticalXMLServiceImpl uncompress failre.Cause by" + e.getMessage());
        } finally {
            try {
                if (outputstream != null) {
                    outputstream.flush();
                    outputstream.close();
                }
                if (inputstream != null) {
                    inputstream.close();
                }
                if (zis != null) {
                    zis.close();
                }
                if (gzis != null) {
                    gzis.close();
                }
                log.info("==========================  uncompress end  =========================");
            } catch (Exception e) {
                log.info(">> AnalyticalXMLServiceImpl uncompress failre.Cause by" + e.getMessage());
            }
        }
    }

    public void setRedisCache(String prefix,String key, Object result) {
        try {
            redisCache.setCacheObject(prefix + key, result);
            redisCache.expire(prefix + key, 1, TimeUnit.HOURS);
        }catch(Exception ex){
           log.error(ex.getMessage()+ex);
        }
    }
}
